// Shaders.h
// 
#pragma once

#include "Utils/StringTools.h"

// https://www.opengl.org/wiki/Compute_eye_space_from_window_space#From_gl_FragCoord

/************************************************************************/
/*  GBuffer                                                             */
/************************************************************************/
// Vertex Shader
const std::string vsGBuffer = STRINGIFY(
varying vec3  v_Normal;
varying vec3  v_Position;

void main()
{				
	vec4 eyespace   = (gl_ModelViewMatrix * gl_Vertex);
	v_Position      = eyespace.xyz;
	v_Normal        = gl_NormalMatrix * gl_Normal;

	gl_Position     = gl_ModelViewProjectionMatrix * gl_Vertex;
	gl_FrontColor   = gl_Color;
}
);

// Fragment Shader
const std::string fsGBuffer = STRINGIFY(
varying vec3 v_Position;
varying vec3 v_Normal;

void main()
{
	float shininess = 60.0;
	vec3 lightDir   = vec3(0.0,0.0,1.0);
	vec3 ambient    = vec3(0.15);
	vec3      n     = normalize(v_Normal);
	vec3      e     = normalize(-v_Position);

	vec3 spec       = vec3(0.0);
	float intensity = max(0.0, dot(n,lightDir));
	if( intensity >= 0.0 )
	{
		vec3 h        = normalize(lightDir+e);
		float intSpec = max(0.0,dot(h,n));
		spec          = vec3(1.0) * pow(intSpec,shininess);
	}

	vec3 dstRGB     = max(gl_Color.xyz*intensity+spec, ambient);
	float dstA      = gl_Color.w;

	float lnZ = (v_Position.z/(100.0-0.01));

	gl_FragData[0] = vec4(n * 0.5 + 0.5,1.0);
	gl_FragData[1] = vec4(v_Position,1.0);
	//gl_FragData[1] = vec4(v_Position.x,v_Position.y,lnZ,1.0);
	gl_FragData[2] = vec4(dstRGB,dstA);
}
);

/************************************************************************/
/*  SSAO                                                                */
/************************************************************************/
// Vertex Shader
const std::string vsSSAO = STRINGIFY(
#version 120\n
uniform vec2 uNearPlaneHalfSize;
uniform mat4 uInvProjMatrix;

varying vec2 texcoord;
varying vec3 eyeDirection;

void main() {
	//   view space position/texcoord:
	texcoord     = gl_Vertex.xy * 0.5 + 0.5;

	// compute eye direction
	//eyeDirection = vec3(2.0 * uNearPlaneHalfSize * texcoord - uNearPlaneHalfSize , -1.0);
	eyeDirection = (uInvProjMatrix * gl_Vertex).xyz;

	//   projected position:
	gl_Position = gl_Vertex;
}
);

// Fragment Shader
const std::string fsSSAO = STRINGIFY(
#version 120\n
uniform sampler2D normalTexture;
uniform sampler2D positionTexture;
uniform float uRadius;
uniform float uPower;

uniform float camera_near;
uniform float camera_far;

int sample_count = 16;
vec2 poisson16[] = vec2[](    // These are the Poisson Disk Samples
	vec2( -0.94201624,  -0.39906216 ),
	vec2(  0.94558609,  -0.76890725 ),
	vec2( -0.094184101, -0.92938870 ),
	vec2(  0.34495938,   0.29387760 ),
	vec2( -0.91588581,   0.45771432 ),
	vec2( -0.81544232,  -0.87912464 ),
	vec2( -0.38277543,   0.27676845 ),
	vec2(  0.97484398,   0.75648379 ),
	vec2(  0.44323325,  -0.97511554 ),
	vec2(  0.53742981,  -0.47373420 ),
	vec2( -0.26496911,  -0.41893023 ),
	vec2(  0.79197514,   0.19090188 ),
	vec2( -0.24188840,   0.99706507 ),
	vec2( -0.81409955,   0.91437590 ),
	vec2(  0.19984126,   0.78641367 ),
	vec2(  0.14383161,  -0.14100790 )
	);

varying vec2 texcoord;
varying vec3 eyeDirection;

/*----------------------------------------------------------------------------*/
vec3 decodeNormal(in vec3 normal)
{
	vec3 n = normal * 2.0 - 1.0;
	return n;
}

/*----------------------------------------------------------------------------*/
vec3 calculatePosition(in vec2 coord)
{
	return texture2D(positionTexture, coord).xyz;
	//vec3 viewRay = vec3( eyeDirection.xy * ((camera_far-camera_near)/ eyeDirection.z), (camera_far-camera_near));

	//float normalizedDepth = texture2D(positionTexture, coord).z;
	//return viewRay * normalizedDepth;
}

/*----------------------------------------------------------------------------*/
void main()
{ 
	vec4 n = texture2D(normalTexture, texcoord);
	
	if( n.a <= 0.0 )
		discard;

	vec3 originPos = calculatePosition(texcoord);
	vec3 normal    = decodeNormal(n.rgb);

	float occlusion = 0.0;
	for (int i = 0; i < sample_count; ++i)
	{
		// sample at an offset specified by the current Poisson-Disk sample and scale it by a radius (has to be in Texture-Space)
		vec2 sampleTexCoord = texcoord + (poisson16[i] * (uRadius));
		//float sampleDepth = texture2D(normalTexture, sampleTexCoord).a;
		vec3 samplePos = calculatePosition(sampleTexCoord);
		vec3 sampleDir = normalize(samplePos - originPos);

		// angle between SURFACE-NORMAL and SAMPLE-DIRECTION (vector from SURFACE-POSITION to SAMPLE-POSITION)
		float NdotS = max(dot(normal, sampleDir), 0);
		// distance between SURFACE-POSITION and SAMPLE-POSITION
		float VPdistSP = distance(originPos, samplePos);

		// a = distance function
		float a = 1.0 - smoothstep(0, 1, VPdistSP);
		// b = dot-Product
		float b = NdotS;

		occlusion += (a * b);
	}

	occlusion = 1.0 - (occlusion / float(sample_count));
	float ao  = pow(occlusion, uPower);   

	//gl_FragColor = vec4(vec3(color.rgb * ao),color.a);
	gl_FragData[0] = vec4(ao);
}
);


/************************************************************************/
/*  Blur Horizontal                                                     */
/************************************************************************/
// Vertex Shader
const std::string vsBlur = STRINGIFY(
uniform float texelWidthOffset;
uniform float texelHeightOffset;

varying vec2 blurCoordinates[5];
//varying vec2 texcoord;

void main()
{
	gl_Position   = gl_Vertex;

	vec2 texcoord = gl_Vertex.xy * 0.5 + 0.5;
	
	vec2 singleStepOffset = vec2(texelWidthOffset, texelHeightOffset);
	blurCoordinates[0] = texcoord;
	blurCoordinates[1] = texcoord + singleStepOffset * 1.407333;
	blurCoordinates[2] = texcoord - singleStepOffset * 1.407333;
	blurCoordinates[3] = texcoord + singleStepOffset * 3.294215;
	blurCoordinates[4] = texcoord - singleStepOffset * 3.294215;
}
);

// Fragment Shader
const std::string fsBlur = STRINGIFY(
uniform sampler2D inputImageTexture;
uniform float texelWidthOffset;
uniform float texelHeightOffset;

varying vec2 blurCoordinates[5];

void main()
{
	vec4 sum = vec4(0.0);
	sum += texture2D(inputImageTexture, blurCoordinates[0]) * 0.204164;
	sum += texture2D(inputImageTexture, blurCoordinates[1]) * 0.304005;
	sum += texture2D(inputImageTexture, blurCoordinates[2]) * 0.304005;
	sum += texture2D(inputImageTexture, blurCoordinates[3]) * 0.093913;
	sum += texture2D(inputImageTexture, blurCoordinates[4]) * 0.093913;
	gl_FragData[0] = sum;
}
);

/************************************************************************/
/*  Final Stage                                                         */
/************************************************************************/
// Vertex Shader
const std::string vsFinalStage = STRINGIFY(
varying vec2 texcoord;

void main()
{
	gl_Position = gl_Vertex;

	texcoord    = gl_Vertex.xy * 0.5 + 0.5;
}
);

// Fragment Shader
const std::string fsFinalStage = STRINGIFY(
uniform sampler2D colorTexture;
uniform sampler2D aoTexture;
uniform sampler2D positionTexture;

varying vec2 texcoord;

void main()
{
	vec4 color  = texture2D( colorTexture, texcoord);
	vec4 ao     = texture2D( aoTexture, texcoord);
	float depth = texture2D( positionTexture, texcoord).w;

	gl_FragColor = vec4( color.rgb * ao.r, color.a);
	gl_FragDepth = depth*0.5+0.5;
	//gl_FragColor = vec4( color.rgb, color.a );
}
);